package org.six11.olive3.util;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.FontFactory;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.DefaultFontMapper;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import org.six11.olive3.client.BasicRenderer;
import org.six11.olive3.client.ForeignPenHandler;
import org.six11.olive3.client.OliveState;
import org.six11.olive3.net.Message;
import org.six11.olive3.net.OliveStreamHandler;
import org.six11.olive3.picturephone.server.PPMessageNames;
import org.six11.olive3.server.DatabaseThing;
import org.six11.olive3.server.SqlStrings;
import org.six11.util2.Debug;

/**
 * Used to generate images of sketches.
 * 
 * @author Gabe Johnson <johnsogg@cmu.edu>
 */
public class ImageUtil {

  public static String TYPE_PDF = "pdf";
  public static String TYPE_PNG = "png";

  public static int DEFAULT_WIDTH = 400;
  public static int DEFAULT_HEIGHT = 400;

  /**
   * @param args
   */
  public static void main(String[] args) throws ServletException {
    System.setProperty("java.awt.headless", "true");
    if (args.length == 0) {
      System.out.println("Syntax: ImageUtil [-pdf -png] id1 id2 ... idN");
    }
    List<String> outputTypes = new ArrayList<String>();
    List<Integer> ids = new ArrayList<Integer>();
    if (args.length == 1 && args[0].equals("all")) {
      outputTypes.add(TYPE_PNG);
    } else {
      for (String s : args) {
        if (s.startsWith("-")) {
          String type = s.substring(1);
          bug("output type: " + type);
          outputTypes.add(type);
        } else {
          try {
            ids.add(new Integer(s));
          } catch (NumberFormatException ex) {
            ex.printStackTrace();
          }
        }
      }
      if (outputTypes.isEmpty()) {
        outputTypes.add(TYPE_PDF);
      }
    }

    MysqlDataSource dbSource = new MysqlDataSource();
    dbSource.setServerName("localhost");
    dbSource.setDatabaseName("sketching_games");
    dbSource.setUser("olive");
    dbSource.setPassword("secret");
    DatabaseThing db = new DatabaseThing(dbSource);

    ImageUtil imageUtil = new ImageUtil(db);
    if (args.length == 1 && args[0].equals("all")) {
      List<Integer> all = db.getIDs("select id from sketch", new String[] {});
      for (int i : all) {
        imageUtil.generateImage(i, TYPE_PNG);
      }
    } else {
      for (int id : ids) {
        for (String type : outputTypes) {
          try {
            imageUtil.generateImage(id, type);
          } catch (ServletException ex) {
            ex.printStackTrace();
          }
        }
      }
    }
  }

  private DatabaseThing db;
  private File baseDir;
  private BasicRenderer renderer;
  private ForeignPenHandler penHandler;
  private OliveState oliveState;

  public ImageUtil(DatabaseThing db) {
    this(db, null);
  }

  public ImageUtil(DatabaseThing db, File baseDir) {
    this.db = db;
    this.baseDir = baseDir;
    this.renderer = new BasicRenderer();
    // renderer.setBackgroundColor(new Color(220, 200, 200));
    MultiState programState = new MultiState();
    this.oliveState = new OliveState(programState);
    this.penHandler = new ForeignPenHandler(oliveState);
  }

  public void generateAllImages() {
    try {
      List<Integer> all = db.getIDs("select id from sketch", new String[] {});
      for (int i : all) {
        generateImage(i, TYPE_PNG);
      }
    } catch (ServletException ex) {
      ex.printStackTrace();
    }
  }

  public void generateImage(int id, String type) throws ServletException {
    generateImage(id, type, DEFAULT_WIDTH, DEFAULT_HEIGHT);
  }

  public void generateImage(int id, String type, int w, int h) throws ServletException {
    String typeStr = "";
    if (type.contains(TYPE_PDF)) {
      typeStr = ".pdf";
    } else if (type.contains(TYPE_PNG)) {
      typeStr = ".png";
    }
    generateImage(id, type, w, h, new File(baseDir, id + typeStr));
  }

  public void generateImage(int id, String type, int w, int h, File f) throws ServletException {
    Map<String, String> sketchData = db.getRow(SqlStrings.FIND_SKETCH, "" + id);
    String messageStringFromDB = sketchData.get("data");
    if (messageStringFromDB == null || messageStringFromDB.length() == 0) {
      bug("Can not generate image for sketch " + id + ": no data.");
      return;
    }
    List<Message> messageFromDB = OliveStreamHandler.parseMessagesFromString(messageStringFromDB);
    if (messageFromDB.size() != 1) {
      return;
    }
    Message sketchMessage = messageFromDB.get(0);
    sketchMessage.addParam(PPMessageNames.PARAM_SKETCH_ID, "" + id);
    penHandler.handleLater(sketchMessage);

    if (type.contains(TYPE_PDF)) {
      generatePdf(f, sketchMessage);
    }
    if (type.contains(TYPE_PNG)) {
      generatePng(f, sketchMessage, w, h);
    }
  }

  public void generatePdf(File file, Message sketchMessage) {
    int w = DEFAULT_WIDTH;
    int h = DEFAULT_HEIGHT;
    Rectangle size = new Rectangle(w, h);
    Document document = new Document(size, 0, 0, 0, 0);

    try {
      PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
      document.open();

      DefaultFontMapper mapper = new DefaultFontMapper();
      FontFactory.registerDirectories();

      PdfContentByte cb = writer.getDirectContent();
      PdfTemplate tp = cb.createTemplate(w, h);
      Graphics2D g2 = tp.createGraphics(w, h, mapper);
      tp.setWidth(w);
      tp.setHeight(h);

      renderer.renderSequences(g2, oliveState.getSequences());

      g2.dispose();
      cb.addTemplate(tp, 0, 0);
    } catch (DocumentException ex) {
      System.err.println(ex.getMessage());
    } catch (IOException ex) {
      System.err.println(ex.getMessage());
    }
    document.close();
    oliveState.clearSurface();
  }

  public void generatePng(File file, Message sketchMessage) {
    generatePng(file, sketchMessage, DEFAULT_WIDTH, DEFAULT_HEIGHT);
  }

  public void generatePng(File file, Message sketchMessage, int w, int h) {
    BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2 = bufferedImage.createGraphics();
    g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON));
    double xs = ((double) w) / ((double) DEFAULT_WIDTH);
    double ys = ((double) h) / ((double) DEFAULT_HEIGHT);
    g2.scale(xs, ys);
    renderer.renderSequences(g2, oliveState.getSequences());
    g2.dispose();
    try {
      ImageIO.write(bufferedImage, "png", file);
    } catch (IOException ex) {
      System.err.println(ex.getMessage());
    }
    oliveState.clearSurface();
  }

  /**
   * @param string
   */
  private static void bug(String what) {
    Debug.out("ImageBroker", what);
  }

}
